package com.chenfan.ccp.plug.log;

import ch.qos.logback.classic.util.LogbackMDCAdapter;
import com.alibaba.ttl.TransmittableThreadLocal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.slf4j.event.Level;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.EnumSet;
import java.util.Map;

public abstract class AbstractLog implements Serializable {


    static {
        customMDC();
    }

    private final String PACKAGE_LINK = ".";
    private final Logger logger;
    private Level LEVEL_FLAG = Level.TRACE;

    public AbstractLog() {
        this.logger = LoggerFactory.getLogger(getClass());
    }

    public AbstractLog(String logName) {
        this.logger = LoggerFactory.getLogger(logName);
    }


    private static void customMDC() {
        try {
            ThreadLocal<Map<String, String>> newThreadLocal = new TransmittableThreadLocal<>();
            LogbackMDCAdapter mdcAdapter = new LogbackMDCAdapter();

            Field copyOnThreadLocal = mdcAdapter.getClass().getDeclaredField("copyOnThreadLocal");
            copyOnThreadLocal.setAccessible(true);
            copyOnThreadLocal.set(mdcAdapter, newThreadLocal);

            Field field = MDC.class.getDeclaredField("mdcAdapter");
            field.setAccessible(true);
            field.set(Field.class, mdcAdapter);
        } catch (Exception e) {

        }
    }

    private String getFormatLog(String className, String format) {
        return String.format("[%s] [%s] => %s", className, Thread.currentThread().getName(), format);
    }

    public void log(String format, Object... arguments) {

        if (format == null || format.length() == 0) {
            return;
        }

        if (level().toInt() < LEVEL_FLAG.toInt()) {
            return;
        }

        record(level(), getFormatLog(logger.getName(), format), arguments);

    }

    private void record(Level level, String format, Object... arguments) {

        if (level() == null || level == null || format == null || format.length() == 0) {
            return;
        }

        switch (level) {
            case DEBUG:
                logger.debug(format, arguments);
                break;
            case TRACE:
                logger.trace(format, arguments);
                break;
            case INFO:
                logger.info(format, arguments);
                break;
            case WARN:
                logger.warn(format, arguments);
                break;
            case ERROR:
                logger.error(format, arguments);
                break;
            default:

        }

    }

    public void log(Level level, String format, Object... arguments) {

        if (level == null || format == null || format.length() == 0) {
            return;
        }
        record(level, getFormatLog(logger.getName(), format), arguments);
    }

    public void log(Level level, String loggerName, String format, Object... arguments) {

        if (level == null || format == null || format.length() == 0) {
            return;
        }
        record(level, getFormatLog(loggerName == null ? logger.getName()
                : loggerName, format), arguments);
    }

    public void log(String loggerName, String format, Object... arguments) {

        if (loggerName == null || loggerName.isEmpty() || format == null || format.length() == 0) {
            return;
        }

        if (level().toInt() >= LEVEL_FLAG.toInt()) {
            record(level(), getFormatLog(loggerName.substring(loggerName.lastIndexOf(PACKAGE_LINK) + 1), format), arguments);
        }
    }

    /**
     * 设置级别
     *
     * @param name
     */
    public void setLevel(String name) {
        EnumSet.allOf(Level.class).stream().filter(x -> x.name().equals(name)).findFirst()
                .ifPresent((x) -> LEVEL_FLAG = x);
    }

    public abstract Level level();


}
